/*    TODO
 *
 * -setting wait() in the decode modules to a smaller time like 200ms will improve unpause latency
 *  and doesn't cost any real computer power
 *
 * -there is a rare segfault, the cause of which is unknown and seems to originate in
 *  another source file
 *
 * -the code is ugly and hacked together, it needs lots of polish
 */

#include <gtk/gtk.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

//do my includes
#include "../include/soundplayer.h"
#include "../include/decodemodules.h"
#include "../resources/GSp.xpm"  //icon

environment_t env;
pthread_t playback;

enum { BTN_PLAY, BTN_PAUSE };
static const char *progname = "GSoundplayer";
void file_select();

struct _gtkdata {
	GtkFileChooser* FileChooser;
	GtkWidget* lbl_song_name;
	GtkFileFilter* filter;
	gchar* filename;
	GtkWidget* show[2];
	GtkWidget* window;
	GSList* multi_file_names;
	GtkWidget* combobx_repeat;
	GtkWidget* combobx_samp_rate;
	int repeat;
} gtkdata = {
	NULL,
	NULL,
	NULL,
	NULL,
	{ NULL, NULL },
	NULL,
	NULL,
	NULL,
	NULL,
	0
}; //initalize all gtk data to NULL or 0


void start_decoding() {
	FILE* input;
	env->format = qualify_filename(gtkdata.filename);
	printf("%s\n", gtkdata.filename);
	input = fopen((const char*)gtkdata.filename, "r");
	struct stat f;
	fstat(fileno(input), &f);
	env->size = f.st_size; //establishing default
	if (pcm_output_init() == 0 && gtkdata.filename != NULL) {
		switch(env->format) {
			case TYPE_WAVE: play_wav(input); break;
			case TYPE_MP3: play_mp3(input); break;
			case TYPE_OGG: env->size = 4096; play_ogg(input); break;
			case TYPE_AAC: play_aac(input); break;
			case TYPE_FLAC: play_flac(input); break;
			default: printf("Invalid or no codec selected\n");
		}
		pcm_output_term();
	}
	fclose(input);

	//handle repeating the current song or switching to the next song
	if (gtkdata.repeat) {
		if (gtkdata.repeat < 9000)
			gtk_combo_box_set_active(GTK_COMBO_BOX(gtkdata.combobx_repeat), --gtkdata.repeat);
		gtk_button_clicked(GTK_BUTTON(gtkdata.show[BTN_PLAY]));
	} else if (gtkdata.multi_file_names != NULL) {
		file_select();  //auto plays next song
	} else {
		//hide pause, show play
		gtk_widget_hide(gtkdata.show[BTN_PAUSE]);
		gtk_widget_show(gtkdata.show[BTN_PLAY]);
	}
}


void play() {
	 switch(env->status) {
		  case SP_ERROR: break;
		  case SP_PAUSE:
				env->status = SP_PLAY;
				gtk_widget_hide(gtkdata.show[BTN_PLAY]);
				gtk_widget_show(gtkdata.show[BTN_PAUSE]);
				break;
		  case SP_STOP: if (playback != 0) pthread_cancel(playback); break;
		  case SP_PLAY: break;
		  case SP_IDLE:
			if (gtkdata.filename != NULL) {
				pthread_create(&playback, NULL, (void *) &start_decoding, NULL);

				//hide play button, show pause button
				gtk_widget_hide(gtkdata.show[BTN_PLAY]);
				gtk_widget_show(gtkdata.show[BTN_PAUSE]);
			}
	 }
}


void multi_file_select() {
	int file_dialog_response = 0;

	//only make the file chooser object once
	if (gtkdata.FileChooser == NULL) {
		gtkdata.FileChooser = (GtkFileChooser*) gtk_file_chooser_dialog_new("File Selection", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
		gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(gtkdata.FileChooser), TRUE);
		gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (gtkdata.FileChooser), gtkdata.filter);
	}

    file_dialog_response = gtk_dialog_run (GTK_DIALOG (gtkdata.FileChooser));
	//hide file selector immediately for faster apparent performance
	gtk_widget_hide (GTK_WIDGET (gtkdata.FileChooser));

	if ( file_dialog_response == GTK_RESPONSE_ACCEPT) {
		//if we chose more songs while one is playing, free the full song list
		if (gtkdata.multi_file_names != NULL) {
			while (gtkdata.multi_file_names != NULL) {
				free(gtkdata.multi_file_names);
				gtkdata.multi_file_names = g_slist_next(gtkdata.multi_file_names);
			}
			g_slist_free(gtkdata.multi_file_names);
		}

		//multiple file selection, thanks to searching on pastebin
		gtkdata.multi_file_names = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkdata.FileChooser));

		//so that you can queue up more files while a song is playing
		if (env->status == SP_IDLE) file_select();
	}
}



void file_select(){
	int size = 0, x = 0, progname_len = len(progname);
	int last_slash = 0;
	char *basename_song = NULL;
	char *file = NULL;

	free(gtkdata.filename); //free it here so that repeated calls to play w/o changing filename work

	if (gtkdata.multi_file_names != NULL) {
		file = (char *) gtkdata.multi_file_names->data;
		gtkdata.multi_file_names = g_slist_next(gtkdata.multi_file_names);
	} else {
		 return;
	}

	while(file[size++] != '\0') if (file[size] == '/') last_slash = size;

	gtkdata.filename = malloc(size);
	basename_song = malloc(size + progname_len + 2);

	x = size;
	while(x != -1) {
		gtkdata.filename[x] = file[x];
		x --;
	}

	g_free(file);

	//set basename_song to gsoundplayer:<songname>
	while (++x < progname_len) basename_song[x] = progname[x];
	basename_song[x++] = ':';
	basename_song[x++] = ' ';
	while(++last_slash <= size) basename_song[x++] = gtkdata.filename[last_slash];
	basename_song[x] = '\0';
	gtk_window_set_title(GTK_WINDOW(gtkdata.window), basename_song);


	if (gtkdata.filename != NULL)
		gtk_label_set_text (GTK_LABEL (gtkdata.lbl_song_name), &basename_song[progname_len + 2]);
	else
		gtk_label_set_text (GTK_LABEL (gtkdata.lbl_song_name), "No File Selected");

	free(basename_song);

	//this function should start playing music once files have been selected, but
	//this also mean we would be in the play() thread and so we will have to deal with that the same way as last time
	//and, of course, make sure we are not already playing anything
	if (env->status == SP_IDLE)
		gtk_button_clicked(GTK_BUTTON(gtkdata.show[BTN_PLAY]));
}


void pause_playback() {
	if (env->status == SP_PLAY) {
		env->status = SP_PAUSE;
	}
	//hide pause, show play
	gtk_widget_hide(gtkdata.show[BTN_PAUSE]);
	gtk_widget_show(gtkdata.show[BTN_PLAY]);
}


void stop_playback() {
	if (playback != 0) {
		env->status = SP_STOP; //first set status to stop
		sleep(1); //wait a second for the decoder to clean up
		if (env->status == SP_STOP) { //kill the thread if it doesn't reset the status
			printf("Killed Thread!\n"); //announce that we had to be forceful
			pthread_cancel(playback);  //kill thread
		}
	}
}

void update_repeat() {
	int active_entry = gtk_combo_box_get_active(GTK_COMBO_BOX(gtkdata.combobx_repeat));

	switch(active_entry) {
		 case 0: gtkdata.repeat = 0; break;
		 case 1: gtkdata.repeat = 1; break;
		 case 2: gtkdata.repeat = 2; break;
		 case 3: gtkdata.repeat = 9001; break; //its over 9000
		 default: gtkdata.repeat = 0;
	}
}

void update_sample_rate() {
	int active_entry = gtk_combo_box_get_active(GTK_COMBO_BOX(gtkdata.combobx_samp_rate));

	switch(active_entry) {
		case 0: env->rate = 96000; break;
		case 1: env->rate = 48000; break;
		case 2: env->rate = 44100; break;
		case 3: env->rate = 22050; break;
		case 4: env->rate = 16000; break;
		case 5: env->rate = 8000; break;
		default: env->rate = 44100;
	}
}

//thank this guy: http://zetcode.com/tutorials/gtktutorial/gtkdialogs/
void view_about_box() {
	GtkWidget *dialog = gtk_about_dialog_new();
	gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "Gsoundplayer");
	gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION);
	gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "A lightweight music player");
	gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), gdk_pixbuf_new_from_xpm_data(GSp_xpm));
	gtk_dialog_run(GTK_DIALOG (dialog));
	gtk_widget_destroy(dialog);
}

int main( int argc, char *argv[] ) {
	env = new_soundplayer_environment();

	//parse commandline arguments
	if (arg_parse(argc, argv) != 0) return 1;

	//tweak the environment
	env->isfifo = 1;

	//new default status for player
	env->status = SP_IDLE;

	gtk_init (&argc, &argv); //initialize gtk

	//declare widgets
	GtkWidget* btn_play = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
	GtkWidget* btn_pause = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE);
	GtkWidget* btn_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);

	gtkdata.show[BTN_PLAY] = btn_play;
	gtkdata.show[BTN_PAUSE] = btn_pause;

	//added a repeat checkbox
	GtkWidget* vbox = gtk_vbox_new(TRUE, 1);
	GtkWidget* bbox = gtk_hbutton_box_new();

	gtkdata.combobx_repeat = gtk_combo_box_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_repeat), "0");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_repeat), "1");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_repeat), "2");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_repeat), "Infinite");
	gtk_combo_box_set_active(GTK_COMBO_BOX(gtkdata.combobx_repeat), 0);

	gtkdata.combobx_samp_rate = gtk_combo_box_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "96000");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "48000");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "44100");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "22050");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "16000");
	gtk_combo_box_append_text(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), "8000");
	gtk_combo_box_set_active(GTK_COMBO_BOX(gtkdata.combobx_samp_rate), 2);

	gtkdata.lbl_song_name = gtk_label_new("No File Selected");
	gtkdata.filter = gtk_file_filter_new();
	gtkdata.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(gtkdata.window), progname);
	gtk_window_set_icon(GTK_WINDOW(gtkdata.window), gdk_pixbuf_new_from_xpm_data(GSp_xpm));

	//a menubar, stupidly complicated to make
	GtkAccelGroup* accel_group = gtk_accel_group_new();
	gtk_window_add_accel_group(GTK_WINDOW(gtkdata.window), accel_group); //keyboard shortcuts
	GtkWidget* menubar = gtk_menu_bar_new();
	GtkWidget* menu_file = gtk_menu_new();
	GtkWidget* menu_help = gtk_menu_new();
	GtkWidget* mb_file_label = gtk_menu_item_new_with_mnemonic("_File");
	GtkWidget* mb_help_label = gtk_menu_item_new_with_mnemonic("_Help");
	GtkWidget* mb_file_open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, accel_group);
	GtkWidget* mb_file_quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
	GtkWidget* mb_help_about = gtk_menu_item_new_with_label("About");

	gtk_menu_item_set_submenu(GTK_MENU_ITEM(mb_file_label), menu_file);
	gtk_menu_item_set_submenu(GTK_MENU_ITEM(mb_help_label), menu_help);

	gtk_menu_shell_append(GTK_MENU_SHELL(menu_file), mb_file_open);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu_file), mb_file_quit);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu_help), mb_help_about);

	gtk_menu_shell_append(GTK_MENU_SHELL(menubar), mb_file_label);
	gtk_menu_shell_append(GTK_MENU_SHELL(menubar), mb_help_label);

	g_signal_connect(G_OBJECT(mb_file_open), "activate", G_CALLBACK(multi_file_select), NULL);
	g_signal_connect(G_OBJECT(mb_help_about), "activate", G_CALLBACK(view_about_box), NULL);
	g_signal_connect(G_OBJECT(mb_file_quit), "activate", G_CALLBACK(gtk_main_quit), NULL);


	//tooltips for everything
	gtk_widget_set_tooltip_text(btn_pause, "Pause the current song");
	gtk_widget_set_tooltip_text(btn_play, "Play the current song");
	gtk_widget_set_tooltip_text(btn_stop, "Stop the current song");
	gtk_widget_set_tooltip_text(gtkdata.combobx_repeat, "Repeat count");
	gtk_widget_set_tooltip_text(gtkdata.combobx_samp_rate, "Set sample rate in Hz");
	gtk_widget_set_tooltip_text(mb_file_open, "Open File");
	gtk_widget_set_tooltip_text(mb_file_quit, "Exit GSoundplayer");


	//helpful stuff for later
	gtk_file_filter_set_name(gtkdata.filter, "Music Files");
	gtk_file_filter_add_mime_type(gtkdata.filter, "audio/x-vorbis+ogg");
	gtk_file_filter_add_mime_type(gtkdata.filter, "audio/x-flac");
	gtk_file_filter_add_mime_type(gtkdata.filter, "audio/x-wav");
	gtk_file_filter_add_mime_type(gtkdata.filter, "audio/aac");
	gtk_file_filter_add_mime_type(gtkdata.filter, "audio/mpeg");

    //create layout
    gtk_button_box_set_layout(GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_EDGE);
    gtk_container_add (GTK_CONTAINER (gtkdata.window), vbox);
    gtk_container_set_border_width (GTK_CONTAINER (gtkdata.window), 0);
    gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), bbox, TRUE, TRUE, 0); //add box
	gtk_box_pack_end (GTK_BOX (vbox), gtkdata.lbl_song_name, TRUE, TRUE, 0); //output label

	gtk_box_pack_start(GTK_BOX (bbox), btn_play, TRUE, TRUE, 0); //add play button
	gtk_box_pack_start(GTK_BOX (bbox), btn_pause, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX (bbox), btn_stop, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX (bbox), gtkdata.combobx_repeat, TRUE, TRUE, 0); //add checkbutton
	gtk_box_pack_start(GTK_BOX (bbox), gtkdata.combobx_samp_rate, TRUE, TRUE, 0);

    //connect signals
    g_signal_connect (gtkdata.window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect (gtkdata.combobx_repeat, "changed", G_CALLBACK (update_repeat), NULL);
    g_signal_connect (gtkdata.combobx_samp_rate, "changed", G_CALLBACK (update_sample_rate), NULL);
    g_signal_connect (btn_play, "clicked", G_CALLBACK (play), NULL);
    g_signal_connect (btn_pause, "clicked", G_CALLBACK (pause_playback), NULL);
    g_signal_connect (btn_stop, "clicked", G_CALLBACK (stop_playback), NULL);

    //show widgets
	gtk_widget_show_all (gtkdata.window);

	//hide a widget
	gtk_widget_hide(btn_pause);

    gtk_main ();

    return 0;
}
